package org.springframework.aop.framework;

import java.lang.reflect.Method;
import java.util.List;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import org.springframework.aop.AdvisedSupport;

/**
 * cglib动态代理
 *
 * @author zqc
 * @date 2022/12/17
 */
public class CglibAopProxy implements AopProxy {
	//可见内部也有一个成员变量advised，和jdk动态代理类一样
	private final AdvisedSupport advised;

	public CglibAopProxy(AdvisedSupport advised) {
		this.advised = advised;
	}

	//CglibAopProxy的getProxy方法和JDK的方法不同。里面实现动态代理类的方式不一样了
	@Override
	public Object getProxy() {
		//创建动态代理增强类
		//Enhancer增强器的意思
		Enhancer enhancer = new Enhancer();
		//增强器也需要传入构造参数
		//1.设置父类，返回的就是我们传入的类（基于父类）
		enhancer.setSuperclass(advised.getTargetSource().getTarget().getClass());
		//2.返回的是传入类的接口
		enhancer.setInterfaces(advised.getTargetSource().getTargetClass());

		//这里设置了一个回调方法，里面设置了一个参数，并且把成员变量赋值进去了
		//这个DynamicAdvisedInterceptor其实也是我们当前类的静态内部类而已
		enhancer.setCallback(new DynamicAdvisedInterceptor(advised));
		return enhancer.create();
	}

	/**
	 * 注意此处的MethodInterceptor是cglib中的接口，advised中的MethodInterceptor的AOP联盟中定义的接口，因此定义此类做适配
	 */

	//JDK动态代理的MethodInterceptor，需要重写invoke方法
	//这个MethodInterceptor是cglib的，重写intercept
	//千万不要混了
	private static class DynamicAdvisedInterceptor implements MethodInterceptor {

		private final AdvisedSupport advised;

		private DynamicAdvisedInterceptor(AdvisedSupport advised) {
			this.advised = advised;
		}

		//重写了一个方法，外面的proxy.explode();方法执行后进入这个方法

		//里面传入4个参数
		//1.被代理类
		//2.被代理类里的被代理方法
		//3.参数
		//4.methodProxy
		@Override
		public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
			// 获取目标对象
			Object target = advised.getTargetSource().getTarget();
			Class<?> targetClass = target.getClass();//获取目标类，目标对象
			Object retVal = null;
			//注意这一句就是获取拦截器链，没啥好说的，就是一个个拿过来
			//这里返回了两个
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

			//这一步我们可以在它下面找到，CglibMethodInvocation（就是jdk的MethodInvocation，这里加个CGLIB）
			//其实也是一个静态内部类
			CglibMethodInvocation methodInvocation = new
					CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy);


			//这里就是判断米传入的对象是否和你的切入点表达式对象匹配，如果匹配，否则怎么---（和JDK动态代理一样）
			if (chain == null || chain.isEmpty()) {
				//如果这个链为空，就是不增强，直接走invoke

				//代理方法
				//methodProxy是静态内部类CglibMethodInvocation的成员变量
				//methodProxy
				retVal = methodProxy.invoke(target, args);
			} else {
				//不为空就走proceed，我们进入这个方法
				retVal = methodInvocation.proceed();
				//这里为什么不用invokeSuper，而是直接用proceed
				//这个proceed是继续的意思，涉及拦截器链
			}
			return retVal;
		}
	}
/*
CglibMethodInvocation扩展ReflectiveMethodInvocation类以支特CGLIBf代理
主要是覆盖了invokeJoinpoint（）方法，如果有原生hodProxy对象，则通过调用MethodProxy#invoke方法否则通过反时调用。
*/

	private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
		private final MethodProxy methodProxy;
		public CglibMethodInvocation(Object proxy, Object target, Method method,
				Object[] arguments, Class<?> targetClass,
				List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
			super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
			this.methodProxy = methodProxy;
			/*
			cglibMethodInvocation扩展ReflectiveMethodInvocation类以支持CGLIB代理
			主要是覆盖了invokeJoinpoint（）方法，如果有MethodProxy对象（传入的第四个参数），
			则通过调用MethodProxy#invoke方法否则通过（method,invoke）反射调用。
			 */
		}

		@Override
		public Object proceed() throws Throwable {
			//这里面的方法会做一个判断，如果methodProxy不为空，
			// 就调用methodProxy.invoke，如果为空，就调用method.invoke

			//这里用的super的proceed，肯定涉及拦截器链
			return super.proceed();
		}
	}
}
